package com.xs.anxingo.server.aop;

import com.xs.anxingo.server.log.BizMonitorLogUtil;
import com.xs.anxingo.server.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * 业务日志aop
 */
@Slf4j
@Aspect
@Component
public class BizLogAcpect {

    @Pointcut("@annotation(com.xs.anxingo.server.aop.BizLog)")
    public void bizLog() {
    }

    /**
     * 定义切入点：对要拦截的方法进行定义与限制，如包、类
     * <p>
     * 1、execution(public * *(..)) 任意的公共方法
     * 2、execution（* set*（..）） 以set开头的所有的方法
     * 3、execution（* com.lingyejun.annotation.LoggerApply.*（..））com.lingyejun.annotation.LoggerApply这个类里的所有的方法
     * 4、execution（* com.lingyejun.annotation.*.*（..））com.lingyejun.annotation包下的所有的类的所有的方法
     * 5、execution（* com.lingyejun.annotation..*.*（..））com.lingyejun.annotation包及子包下所有的类的所有的方法
     * 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及子包下所有的类的有三个参数，第一个参数为String类型，第二个参数为任意类型，第三个参数为Long类型的方法
     * 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
     */
    @Around("bizLog()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {

        long start = System.currentTimeMillis();
        boolean runSuccess = true;

        BizLog bizLog = getDeclaredAnnotation(joinPoint);
        Object result = null;

        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            runSuccess = false;
            log.error("{}", e);
            throw e;
        } finally {
            Map resultMap = this.getResultMap(result);
            String respCodeKey = bizLog.respCodeKey();
            String resultCodeKey = bizLog.resultCodeKey();

            long costTime = System.currentTimeMillis() - start;
            String logType = bizLog.logType();
            String respCode = this.getCodeByKey(resultMap, respCodeKey);
            String resultCode = this.getCodeByKey(resultMap, resultCodeKey);
            String className = joinPoint.getSignature().getDeclaringType().getName();
            String methodName = joinPoint.getSignature().getName();
            String platform = null;//TODO platform取值

            //打印业务日志
            BizMonitorLogUtil.logBizInfo(costTime, runSuccess, logType, respCode, resultCode, className, methodName, platform);

        }
    }

    private Map getResultMap(Object result) {
        try {
            if (result != null && result instanceof String) {
                Map<String, Object> resultMap = JsonUtil.toMap(result.toString());
                return resultMap;
            }
        } catch (Exception e) {
        }
        return null;
    }

    private String getCodeByKey(Map resultMap, String key) {
        if (resultMap == null) {
            return null;
        }
        if (StringUtils.isBlank(key)) {
            return null;
        }
        Object code = null;
        do {
            code = resultMap.get(key.substring(key.indexOf(".")));
            if (code != null) {
                resultMap = (Map) code;
            }
            key = key.substring(key.indexOf("."), key.length());
        } while (key.contains("."));

        if (code == null) {
            return null;
        } else {
            return code.toString();
        }
    }


    private BizLog getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型（重载）获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到方法定义的注解信息
        BizLog annotation = objMethod.getDeclaredAnnotation(BizLog.class);
        // 返回
        return annotation;
    }

}
